package gates;


import java.awt.Graphics;
import java.util.*;

import master.ComplexNumber;
import master.QuantumRegister;


/**
 * The Controlled Phase shift is an extension of the normal phase shift and works in a similar way.
 * The difference is that it will only perform the Phase Shift if the control bit is set to one.
 * 
 * @author Oliver
 *
 */
public class ControlledPhaseShift extends QuantumGate {

	public static IllegalArgumentException GateException =
			new IllegalArgumentException("applying wrong type of gate to register");

	int cBit, tBit;
	float phase;

	/**
	 * 
	 * @param cBit		- control bit
	 * @param tBit		- target bit
	 * @param phase		- desired phase shift
	 */
	public ControlledPhaseShift(int cBit, int tBit, float phase) {
		this.cBit = cBit;
		this.tBit = tBit;
		this.phase = phase;

	}

	/**
	 * shifts an integer left by one
	 * @param n
	 * @return 2^n
	 */
	public int power2(int n) {
		return 1 << n;
	}

	/**
	 * 
	 * @param register - the register to act on
	 */
	public void actOnReg(QuantumRegister register) {
		List<Integer> affectedBits = new LinkedList<Integer>();

		// find all states that have the control bit set to one and add them to the list
		for (int t = 0; t < register.getDimension(); t++) {
			if ( (t & power2(cBit)) <= 0 )
				continue;
			if (!affectedBits.contains(t - power2(tBit)))
				affectedBits.add(t);
		}

		// go through the affected eigenstates and perform the phase shift
		for ( int t : affectedBits) {
			if ( (register.getCoefficient(t).magnitudeFloat() != 0.0f)){

				float phase1 = (float) (phase + register.getCoefficient(t).phaseFloat());
				float mag = register.getCoefficient(t).magnitudeFloat();

				ComplexNumber temp = ComplexNumber.expi( mag ,phase1 );
				register.setCoefficient(temp, t);
			}
		}
	}

	/**
	 * used for visual representation of the gate in the UI
	 */
	

	/**
	 * Returns a list with all the qBits the register acts on
	 */
	public int[] getActingBits() {
		int[] bits = new int[2];
		bits[0] = cBit+1;
		bits[1] = tBit+1;
		return bits;
	}

	/**
	 * prints out the register as states with coefficients
	 * used only for testing
	 * @param reg
	 */
	public static void printOut(QuantumRegister reg){
		for (int n=0;n<reg.getDimension();n++){
			//if(reg.getCoefficient(n).magnitudeFloat()>0)
			System.out.println(" " + reg.getCoefficient(n) + "|" + Integer.toBinaryString(n) + "("+ n +")>");
		}
		System.out.println("");
	}

	/**
	 * code snippet used to test the gate
	 */
	public static void main(String[] args){
		QuantumRegister reg = new QuantumRegister(5);
		QuantumGate pgate = new ControlledPhaseShift(0, 3, (float) Math.PI/4);
		QuantumGate hgate = new Hadamard(3);
		reg.setToEigenstate(21);
		printOut(reg);
		hgate.actOnReg(reg);
		printOut(reg);
		pgate.actOnReg(reg);
		printOut(reg);

	}

}
